package cn.org.seventh.web.tag;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
import org.springframework.context.support.ResourceBundleMessageSource;

import cn.org.seventh.common.StringUtil;
import cn.org.seventh.web.common.RequestUtil;

/**
 * @jsp.tag name="pager"
 * @author <a href="mailto:ji.li@symbio-group.com">Andy</a>
 * @since Dec 3, 2007 10:55:30 AM
 */
public class PagerTag extends BaseTag {

	private static final int DEFAULT_LEVEL = 99;

	private final transient Log log = LogFactory.getLog(PagerTag.class);

	private static final long serialVersionUID = -2337329123563861600L;

	protected static final String DEFAULT_PID = "pid";

	protected static final int DEFAULT_PAGE_LIMIT = 20;

	protected static final int DEFAULT_DELTA = 10;

	protected boolean browseMode = false;

	protected String itemsSuffix;

	protected long totalCount;

	protected int pageLimit = DEFAULT_PAGE_LIMIT;

	protected int delta = DEFAULT_DELTA;

	protected long currentPage;

	protected String paramId = DEFAULT_PID;

	protected String firstLabel;

	protected String firstImage;

	protected String prevLabel;

	protected String prevImage;

	protected String nextLabel;

	protected String nextImage;

	protected String lastLabel;

	protected String lastImage;

	protected String labelBefore;

	protected String labelAfter;

	protected String numberBefore;

	protected String numberAfter;

	protected String baseUrl;

	protected String styleClass;

	protected int level = DEFAULT_LEVEL;

	/**
	 * @jsp.attribute required = "false"
	 */
	public void setLevel(int level) {
		this.level = level;
	}

	/**
	 * @jsp.attribute required = "false"
	 */
	public void setBrowseMode(boolean browseMode) {
		this.browseMode = browseMode;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param styleClass
	 */
	public void setStyleClass(String styleClass) {
		this.styleClass = styleClass;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param firstImage
	 */
	public void setFirstImage(String firstImage) {
		this.firstImage = firstImage;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param lastImage
	 */
	public void setLastImage(String lastImage) {
		this.lastImage = lastImage;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param nextImage
	 */
	public void setNextImage(String nextImage) {
		this.nextImage = nextImage;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param prevImage
	 */
	public void setPrevImage(String prevImage) {
		this.prevImage = prevImage;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param delta
	 */
	public void setDelta(int delta) {
		if (delta > 0) {
			this.delta = delta;
		}
	}

	/**
	 * @jsp.attribute required = "false" rtexprvalue="true"
	 * @param itemsSuffix
	 */
	public void setItemsSuffix(String itemsSuffix) {
		try {
			this.itemsSuffix = (String) ExpressionEvaluatorManager
					.evaluate("itemsSuffix", itemsSuffix, String.class, this,
							pageContext);
		} catch (JspException e) {
			log.debug(e);
		}
	}

	/**
	 * @jsp.attribute required = "true" rtexprvalue="true"
	 * @param baseUrl
	 */
	public void setBaseUrl(String baseUrl) {
		try {
			this.baseUrl = (String) ExpressionEvaluatorManager.evaluate(
					"baseUrl", baseUrl, String.class, this, pageContext);
		} catch (JspException e) {
			log.debug(e);
		}
	}

	/**
	 * @jsp.attribute required = "false" rtexprvalue="true"
	 * @param currentPage
	 */
	public void setCurrentPage(String currentPage) {
		try {
			this.currentPage = ((Long) ExpressionEvaluatorManager.evaluate(
					"currentPage", currentPage, Long.class, this, pageContext))
					.longValue() - 1;
		} catch (JspException e) {
			log.debug(e);
		}
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param firstLabel
	 */
	public void setFirstLabel(String firstLabel) {
		this.firstLabel = firstLabel;
	}

	public String getNumberAfter() {
		if (numberAfter == null) {
			return labelAfter;
		}
		return numberAfter;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param numberAfter
	 */
	public void setNumberAfter(String numberAfter) {
		this.numberAfter = numberAfter;
	}

	public String getNumberBefore() {
		if (numberBefore == null) {
			return labelBefore;
		}
		return numberBefore;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param numberBefore
	 */
	public void setNumberBefore(String numberBefore) {
		this.numberBefore = numberBefore;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param labelAfter
	 */
	public void setLabelAfter(String labelAfter) {
		this.labelAfter = labelAfter;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param labelBefore
	 */
	public void setLabelBefore(String labelBefore) {
		this.labelBefore = labelBefore;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param lastLabel
	 */
	public void setLastLabel(String lastLabel) {
		this.lastLabel = lastLabel;
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param nextLabel
	 */
	public void setNextLabel(String nextLabel) {
		this.nextLabel = nextLabel;
	}

	/**
	 * @jsp.attribute required = "false" rtexprvalue="true"
	 * @param pageLimit
	 */
	public void setPageLimit(String pageLimit) {
		try {
			this.pageLimit = ((Integer) ExpressionEvaluatorManager.evaluate(
					"pageLimit", pageLimit, Integer.class, this, pageContext))
					.intValue();
		} catch (JspException e) {
			log.debug(e);
		}
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param paramId
	 */
	public void setParamId(String paramId) {
		if (paramId != null) {
			this.paramId = paramId;
		}
	}

	/**
	 * @jsp.attribute required = "false"
	 * @param prevLabel
	 */
	public void setPrevLabel(String prevLabel) {
		this.prevLabel = prevLabel;
	}

	/**
	 * @jsp.attribute required = "false" rtexprvalue="true"
	 * @param totalCount
	 */
	public void setTotalCount(String totalCount) {
		try {
			this.totalCount = ((Long) ExpressionEvaluatorManager.evaluate(
					"totalCount", totalCount, Long.class, this, pageContext))
					.longValue();
		} catch (JspException e) {
			log.debug(e);
		}
	}

	public int doEndTag() throws JspException {
		JspWriter out = pageContext.getOut();
		long pageCount = totalCount / pageLimit;
		if (totalCount % pageLimit != 0) {
			pageCount++;
		}
		if (currentPage < 0) {
			currentPage = 0;
		} else if (currentPage > pageCount - 1) {
			currentPage = pageCount - 1;
		}
		try {
			ResourceBundleMessageSource messageSource = getMessageSource();
			Locale locale = pageContext.getRequest().getLocale();
			out.print("<div");
			if (styleClass != null) {
				out.print(" class=\"" + styleClass + "\"");
			}
			out.print(">");
			if (level >= 3) {
				out.print(messageSource.getMessage("label.pageNo", null,
						"No. ", locale));
				out.print(currentPage + 1);
				out.print("/");
				out.print(pageCount);
				out.print(messageSource.getMessage("label.page", null, " Page",
						locale));
				/*
				 * out.print(messageSource.getMessage("label.pageOrder", null,
				 * "Page ", locale)); out.print(currentPage + 1);
				 * out.print("/"); out.print(pageCount);
				 */
			}
			if (level >= 4) {
				out.print("&nbsp;");
				out.print(messageSource.getMessage("label.perPage", null,
						"Each Page ", locale));
				out.print(pageLimit);
				if (itemsSuffix == null) {
					out.print(messageSource.getMessage("label.itemsSuffix",
							null, " item(s).", locale));
				} else {
					out.print(messageSource.getMessage(itemsSuffix, null,
							itemsSuffix, locale));
				}
				out.print("&nbsp;");
				out.print(messageSource.getMessage("label.total", null,
						"Total ", locale));
				out.print(totalCount);
				if (itemsSuffix == null) {
					out.print(messageSource.getMessage("label.itemsSuffix",
							null, " item(s).", locale));
				} else {
					out.print(messageSource.getMessage(itemsSuffix, null,
							itemsSuffix, locale));
				}
			}
			if (pageCount > 1) {
				out.print("&nbsp;");
				// out.print(messageSource.getMessage("label.pageDivide", null,
				// "Page:", locale));
				long firstPage = 0;
				long lastPage = pageCount - 1;
				if (lastPage < 0) {
					lastPage = 0;
				}
				HashMap params = (HashMap) pageContext.getAttribute(String
						.valueOf(hashCode()));
				if (params != null) {
					for (Iterator all = params.keySet().iterator(); all
							.hasNext();) {
						String paramName = (String) all.next();
						String paramValue = (String) params.get(paramName);
						if (!StringUtil.isEmpty(paramValue)) {
							baseUrl = RequestUtil.appendParameter(baseUrl,
									paramName, params.get(paramName));
						}
					}
				}
				if (!browseMode) {
					doJumppingMode(out, pageCount, messageSource, locale,
							firstPage, lastPage);
				} else {
					doBrowseMode(out, pageCount, firstPage, lastPage);
				}
			}
			out.println("</div>");
		} catch (IOException e) {
			log.debug(e);
		}
		pageContext.removeAttribute(String.valueOf(hashCode()));
		return super.doEndTag();
	}

	private void doJumppingMode(JspWriter out, long pageCount,
			ResourceBundleMessageSource messageSource, Locale locale,
			long firstPage, long lastPage) throws IOException {
		long prevPage = currentPage - 1;
		if (prevPage < 0) {
			prevPage = 0;
		}
		long nextPage = currentPage + 1;
		if (nextPage > pageCount - 1) {
			nextPage = pageCount - 1;
		}
		String url = null;
		if (level >= 2) {
			// Begin of firstPage
			url = RequestUtil.appendParameter(baseUrl, paramId, String
					.valueOf(firstPage + 1));
			if (currentPage > firstPage) {
				String style = "";
				if (null != styleClass) {
					style += "class='" + styleClass + "'";
				}
				out.print("<a href=\"" + url + "\" " + style + ">");
			}
			if (firstImage != null) {
				out.print("<img src=\"" + firstImage + "\" border=\"0\"/>");
			} else {
				if (labelBefore != null) {
					out.print(labelBefore);
				}
				if (firstLabel != null) {
					out.print(firstLabel);
				} else {
					out.print(messageSource.getMessage("label.firstPage", null,
							"First Page", locale));
				}
				if (labelAfter != null) {
					out.print(labelAfter);
				}
			}
			if (currentPage > firstPage) {
				out.print("</a>");
			}
			out.print("&nbsp;");
			// End of firstPage
		}
		// Begin of prevPage
		url = RequestUtil.appendParameter(baseUrl, paramId, String
				.valueOf(prevPage + 1));
		if (currentPage != prevPage) {
			String style = "";
			if (null != styleClass) {
				style += "class='" + styleClass + "'";
			}
			out.print("<a href=\"" + url + "\" " + style + ">");
		}
		if (prevImage != null) {
			out.print("<img src=\"" + prevImage + "\" border=\"0\"/>");
		} else {
			if (labelBefore != null) {
				out.print(labelBefore);
			}
			if (prevLabel != null) {
				out.print(prevLabel);
			} else {
				out.print(messageSource.getMessage("label.prevPage", null,
						"Previous Page", locale));
			}
			if (labelAfter != null) {
				out.print(labelAfter);
			}
		}
		if (currentPage != prevPage) {
			out.print("</a>");
		}
		out.print("&nbsp;");
		// End of prevPage
		// Begin of nextPage
		url = RequestUtil.appendParameter(baseUrl, paramId, String
				.valueOf(nextPage + 1));
		if (currentPage != nextPage) {
			String style = "";
			if (null != styleClass) {
				style += "class='" + styleClass + "'";
			}
			out.print("<a href=\"" + url + "\" " + style + ">");
		}
		if (nextImage != null) {
			out.print("<img src=\"" + nextImage + "\" border=\"0\"/>");
		} else {
			if (labelBefore != null) {
				out.print(labelBefore);
			}
			if (nextLabel != null) {
				out.print(nextLabel);
			} else {
				out.print(messageSource.getMessage("label.nextPage", null,
						"Next Page", locale));
			}
			if (labelAfter != null) {
				out.print(labelAfter);
			}
		}
		if (currentPage != nextPage) {
			out.print("</a>");
		}
		out.print("&nbsp;");
		// End of nextPage
		if (level >= 2) {
			// Begin of lastPage
			url = RequestUtil.appendParameter(baseUrl, paramId, String
					.valueOf(lastPage + 1));
			if (currentPage < lastPage) {
				String style = "";
				if (null != styleClass) {
					style += "class='" + styleClass + "'";
				}
				out.print("<a href=\"" + url + "\" " + style + ">");
			}
			if (lastImage != null) {
				out.print("<img src=\"" + lastImage + "\" border=\"0\"/>");
			} else {
				if (labelBefore != null) {
					out.print(labelBefore);
				}
				if (lastLabel != null) {
					out.print(lastLabel);
				} else {
					out.print(messageSource.getMessage("label.lastPage", null,
							"Last Page", locale));
				}
				if (labelAfter != null) {
					out.print(labelAfter);
				}
			}
			if (currentPage < lastPage) {
				out.print("</a>");
			}
			out.print("&nbsp;");
			// End of lastPage
		}
	}

	private void doBrowseMode(JspWriter out, long pageCount, long firstPage,
			long lastPage) throws IOException {
		long startPage = (currentPage / delta) * delta;
		long endPage = startPage + delta - 1;
		if (endPage > pageCount - 1) {
			endPage = pageCount - 1;
		}
		long prevPage = startPage - delta;
		if (prevPage < 0) {
			prevPage = 0;
		}
		long nextPage = startPage + delta;
		if (nextPage > pageCount - 1) {
			nextPage = pageCount - 1;
		}
		// Begin of firstPage
		String url = RequestUtil.appendParameter(baseUrl, paramId, String
				.valueOf(firstPage + 1));
		if (startPage != firstPage) {
			String style = "";
			if (null != styleClass) {
				style += "class='" + styleClass + "'";
			}
			out.print("<a href=\"" + url + "\" " + style + ">");
		}
		if (firstImage != null) {
			out.print("<img src=\"" + firstImage + "\"/>");
		} else {
			if (labelBefore != null) {
				out.print(labelBefore);
			}
			if (firstLabel != null) {
				out.print(firstLabel);
			} else {
				out.print(firstPage + 1);
			}
			if (labelAfter != null) {
				out.print(labelAfter);
			}
		}
		if (startPage != firstPage) {
			out.print("</a>");
		}
		out.print("&nbsp;");
		// End of firstPage
		// Begin of prevPage
		url = RequestUtil.appendParameter(baseUrl, paramId, String
				.valueOf(prevPage + 1));
		if (startPage != prevPage) {
			String style = "";
			if (null != styleClass) {
				style += "class='" + styleClass + "'";
			}
			out.print("<a href=\"" + url + "\" " + style + ">");
		}
		if (prevImage != null) {
			out.print("<img src=\"" + prevImage + "\"/>");
		} else {
			if (labelBefore != null) {
				out.print(labelBefore);
			}
			if (prevLabel != null) {
				out.print(prevLabel);
			} else {
				out.print(prevPage + 1);
			}
			if (labelAfter != null) {
				out.print(labelAfter);
			}
		}
		if (startPage != prevPage) {
			out.print("</a>");
		}
		out.print("&nbsp;");
		// End of prevPage
		for (long i = startPage; i <= endPage; i++) {
			url = RequestUtil.appendParameter(baseUrl, paramId, String
					.valueOf(i + 1));
			if (currentPage != i) {
				String style = "";
				if (null != styleClass) {
					style += "class='" + styleClass + "'";
				}
				out.print("<a href=\"" + url + "\" " + style + ">");
			}
			if (getNumberBefore() != null) {
				out.print(getNumberBefore());
			}
			out.print(i + 1);
			if (getNumberAfter() != null) {
				out.print(getNumberAfter());
			}
			if (currentPage != i) {
				out.print("</a>");
			}
			out.print("&nbsp;");
		}
		// Begin of nextPage
		url = RequestUtil.appendParameter(baseUrl, paramId, String
				.valueOf(nextPage + 1));
		if (nextPage < lastPage) {
			String style = "";
			if (null != styleClass) {
				style += "class='" + styleClass + "'";
			}
			out.print("<a href=\"" + url + "\" " + style + ">");
		}
		if (nextImage != null) {
			out.print("<img src=\"" + nextImage + "\"/>");
		} else {
			if (labelBefore != null) {
				out.print(labelBefore);
			}
			if (nextLabel != null) {
				out.print(nextLabel);
			} else {
				out.print(nextPage + 1);
			}
			if (labelAfter != null) {
				out.print(labelAfter);
			}
		}
		if (nextPage < lastPage) {
			out.print("</a>");
		}
		out.print("&nbsp;");
		// End of nextPage
		// Begin of lastPage
		url = RequestUtil.appendParameter(baseUrl, paramId, String
				.valueOf(lastPage + 1));
		if (startPage + delta < lastPage) {
			String style = "";
			if (null != styleClass) {
				style += "class='" + styleClass + "'";
			}
			out.print("<a href=\"" + url + "\" " + style + ">");
		}
		if (lastImage != null) {
			out.print("<img src=\"" + lastImage + "\"/>");
		} else {
			if (labelBefore != null) {
				out.print(labelBefore);
			}
			if (lastLabel != null) {
				out.print(lastLabel);
			} else {
				out.print(lastPage + 1);
			}
			if (labelAfter != null) {
				out.print(labelAfter);
			}
		}
		if (startPage + delta < lastPage) {
			out.print("</a>");
		}
		// End of lastPage
	}
}
